<!--
 * @Descripttion: 
 * @Author: 张正兴
 * @Date: 2022-03-28 15:59:08
 * @LastEditors: 张正兴
-->
<template>
  <div id="container" ref="containerRef" style="position: relative"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import { controls } from "./plugin/controls";
import { onMounted, ref } from "vue";
import LightSet from "@/views/threeDimensional/light/lightMod";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
import { loadCity } from "./mode/sceneModel";
import { animations } from "./params";
import scene from './scene'

const containerRef = ref<any>();

// 时钟
const clock = new THREE.Clock();

// 相机
function loadCamera(camera: any, width: number, height: number) {
  camera = new THREE.PerspectiveCamera(60, width / height, 1, 2000);
  camera.position.set(-60, 60, 160); //设置相机位置
  camera.lookAt(new THREE.Vector3(0, 0, 0)); //让相机指向原点
  //this.camera.up.set(0, 0, 1); //改变轴方向向上改成了z轴
  return camera;
}

// 灯光
function loadLights(scene: any) {
  const lightSet = new LightSet(scene);
  // 平行光
  lightSet.directionalLight([85, 85, 0], 1.2);

  // 平行光
  lightSet.directionalLight([-85, -85, 0], 0.2);

  // 环境光
  lightSet.ambientLight(0.5);
}

// 模型
function loadModel(scene: any) {
  loadCity(scene);
}

// 辅助线
function loadAuxiliary(scene: any) {
  // 横竖坐标
  const axis = new THREE.AxesHelper(100);
  //THREE.AxesHelper
  scene.add(axis);

  // 网格
  const size = 100;
  const divisions = 20;
  const gridHelper = new THREE.GridHelper(size, divisions);
  scene.add(gridHelper);
}

// 渲染器
function loadRenderer(width: number, height: number) {
  const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.shadowMap.enabled = true; // 开启阴影
  renderer.shadowMap.type = THREE.BasicShadowMap;
  renderer.setSize(width, height);
  return renderer;
}

// 后期处理
function loadComposer(width: number, height: number, renderer: any, scene: any, camera: any) {
  const BLOOM_SCENE = 1;
  const bloomLayer = new THREE.Layers();
  bloomLayer.set(BLOOM_SCENE);

  const darkMaterial = new THREE.MeshBasicMaterial({ color: "black" });
  const materials: any = {};

  const renderScene = new RenderPass(scene, camera);

  const bloomPass = new UnrealBloomPass(new THREE.Vector2(width, height), 1.5, 0.4, 0.85);
  bloomPass.threshold = 0;
  bloomPass.strength = 0.5;
  bloomPass.radius = 0;

  const bloomComposer = new EffectComposer(renderer);
  bloomComposer.renderToScreen = false;
  bloomComposer.addPass(renderScene);
  bloomComposer.addPass(bloomPass);

  // 选中发光
  const outlinePass = new OutlinePass(new THREE.Vector2(width, height), scene, camera);
  outlinePass.visibleEdgeColor.set("#e7b80d");
  bloomComposer.addPass(outlinePass);

  // 抗锯齿
  const effectFXAA = new ShaderPass(FXAAShader);
  effectFXAA.uniforms["resolution"].value.set(1 / width, 1 / height);
  bloomComposer.addPass(effectFXAA);

  const finalPass = new ShaderPass(
    new THREE.ShaderMaterial({
      uniforms: {
        baseTexture: { value: null },
        bloomTexture: { value: bloomComposer.renderTarget2.texture },
      },
      vertexShader: document.getElementById("vertexshader")?.textContent as any,
      fragmentShader: document.getElementById("fragmentshader")?.textContent as any,
      defines: {},
    }),
    "baseTexture"
  );

  const finalComposer = new EffectComposer(renderer);
  finalComposer.addPass(renderScene);
  finalComposer.addPass(finalPass);

  function render() {
    renderBloom();
    finalComposer.render();
  }

  function renderBloom() {
    scene.traverse(darkenNonBloomed);
    bloomComposer.render();
    scene.traverse(restoreMaterial);
  }

  function darkenNonBloomed(obj: any) {
    if (obj.isMesh && bloomLayer.test(obj.layers) === false) {
      materials[obj.uuid] = obj.material;
      obj.material = darkMaterial;
    }
  }

  function restoreMaterial(obj: any) {
    if (materials[obj.uuid]) {
      obj.material = materials[obj.uuid];
      delete materials[obj.uuid];
    }
  }

  return { render, outlinePass };
}

// 射线检测
function loadInitRay(event: any, camera: any, mainCanvas: any, scene: any, outlinePass: any) {
  // 将屏幕坐标转为标准设备坐标(支持画布非全屏的情况)
  let x = ((event.clientX - mainCanvas.getBoundingClientRect().left) / mainCanvas.offsetWidth) * 2 - 1; // 设备横坐标
  let y = -((event.clientY - mainCanvas.getBoundingClientRect().top) / mainCanvas.offsetHeight) * 2 + 1; // 设备纵坐标

  let standardVector = new THREE.Vector3(x, y, 1); // 设备坐标

  // 标准设备坐标转为世界坐标
  let worldVector = standardVector.unproject(camera);

  // 射线投射方向单位向量(worldVector坐标减相机位置坐标)
  let ray = worldVector.sub(camera.position).normalize();

  // 创建射线投射器对象
  let rayCaster = new THREE.Raycaster(camera.position, ray);

  const pointer = new THREE.Vector2();
  pointer.x = x;
  pointer.y = y;
  rayCaster.setFromCamera(pointer, camera);

  // 返回射线选中的对象数组(第二个参数默认值是false，意为是否遍历图形内部的所有子图形)
  let intersects = rayCaster.intersectObjects(scene.children, true);

  if (intersects.length > 0 && intersects[0].object.name != "sprite") {
    // 射线拾取的首个对象
    let currObj: any = intersects[0];
    console.log("选中的网格=>", currObj);
    // 选中发光的物体
    const selects: any[] = [];
    currObj.object.parent.children.forEach((item: any) => {
      if (item.name != "sprite") {
        selects.push(item);
      }
    });
    outlinePass.selectedObjects = selects;

    // 调用组件自定义的click方法
    if (currObj.object.parent?.parent?.onClick) {
      currObj.object.parent.parent.onClick();
    }
  } else {
    outlinePass.selectedObjects = []
  }
}

// 窗口变化重新渲染
function onWindowResize(camera: any, renderer: any) {
  const container: any = containerRef.value;
  const width = container.clientWidth;
  const height = container.clientHeight;

  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);
}

onMounted(() => {

  loadModel(scene);         // 加载全局模型
  // loadAuxiliary(scene);  // 辅助线
  loadLights(scene);        // 加载全局灯光

  // 渲染器
  const container: any = containerRef.value;
  const width = container.clientWidth;
  const height = container.clientHeight;

  // 相机
  const camera = loadCamera(container, width, height);
  camera.name = "透视相机"

  // 渲染器
  const renderer = loadRenderer(width, height);
  container.appendChild(renderer.domElement);

  // 事件
  container.onmousedown = (e: any) => {
    loadInitRay(e, camera, container, scene, outlinePass);
  };

  // 相机插件
  controls(camera, renderer.domElement);

  // 后期处理
  const { render, outlinePass } = loadComposer(width, height, renderer, scene, camera);

  // 窗口
  window.addEventListener("resize", () => onWindowResize(camera, renderer));

  const animate = () => {
    requestAnimationFrame(animate);

    // 开启动画
    if (animations.length > 0) {
      const delta = clock.getDelta();
      animations.forEach(item => {
        item.update(delta)
      })
    }

    //renderer.render(scene, camera);
    render();
  };
  animate();
});
</script>

<style>
#container {
  width: 100%;
  height: 100%;
}
</style>
